package net.xdclass.oss.handler.file.impl;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.region.Region;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.oss.config.TencentCosProperty;
import net.xdclass.oss.enums.AttachmentType;
import net.xdclass.common.exception.service.FileOperationException;
import net.xdclass.oss.handler.file.FileHandler;
import net.xdclass.oss.handler.file.FilePathDescriptor;
import net.xdclass.oss.model.UploadResult;
import net.xdclass.oss.utils.ImageUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;

import static net.xdclass.common.constant.Cons.URL_SEPARATOR;

@Slf4j
@Component
public class TencentCosFileHandler implements FileHandler {

    @Autowired
    private TencentCosProperty tencentCosProperty;

    /**
     * Uploads file.
     *
     * @param file multipart file must not be null
     * @return upload result
     * @throws FileOperationException throws when fail to upload the file
     */
    @Override
    public UploadResult upload(MultipartFile file) {
        Assert.notNull(file, "Multipart file must not be null");

        // Get config
        String protocol = tencentCosProperty.getProtocolOrDefault();
        String domain = tencentCosProperty.getDomainOrDefault();
        String region = tencentCosProperty.getRegionOrNonNull();
        String secretId = tencentCosProperty.getSecretIdOrNonNull();
        String secretKey = tencentCosProperty.getSecretKeyOrNonNull();
        String bucketName = tencentCosProperty.getBucketNameOrNonNull();
        String source = tencentCosProperty.getSourceOrDefault();
        String styleRule = tencentCosProperty.getStyleRuleOrNonNull();
        String thumbnailStyleRule = tencentCosProperty.getThumbnailStyleRuleOrNonNull();

        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
        Region regionConfig = new Region(region);
        ClientConfig clientConfig = new ClientConfig(regionConfig);

        // Init OSS client
        COSClient cosClient = new COSClient(cred, clientConfig);

        StringBuilder basePath = new StringBuilder(protocol);

        if (StringUtils.isNotEmpty(domain)) {
            basePath.append(domain)
                    .append(URL_SEPARATOR);
        } else {
            basePath.append(bucketName)
                    .append(".cos.")
                    .append(region)
                    .append(".myqcloud.com")
                    .append(URL_SEPARATOR);
        }

        try {
            LocalDateTime now = LocalDateTime.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
            String time = formatter.format(now);
            String path = source + File.separator +time;
            FilePathDescriptor pathDescriptor = new FilePathDescriptor.Builder()
                    .setBasePath(basePath.toString())
                    .setSubPath(path)
                    .setAutomaticRename(true)
                    .setRenamePredicate(relativePath -> true)
                    .setOriginalName(file.getOriginalFilename())
                    .build();

            // Upload
            ObjectMetadata objectMetadata = new ObjectMetadata();
            //提前告知输入流的长度, 否则可能导致 oom
            objectMetadata.setContentLength(file.getSize());
            // 设置 Content type, 默认是 application/octet-stream
            objectMetadata.setContentType(file.getContentType());
            PutObjectResult putObjectResponseFromInputStream = cosClient
                    .putObject(bucketName, pathDescriptor.getRelativePath(), file.getInputStream(),
                            objectMetadata);
            if (putObjectResponseFromInputStream == null) {
                throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到腾讯云失败 ");
            }
            String fullPath = pathDescriptor.getFullPath();
            // Response result
            UploadResult uploadResult = new UploadResult();
            uploadResult.setFilename(pathDescriptor.getName());
            uploadResult
                    .setFilePath(StringUtils.isBlank(styleRule) ? fullPath : fullPath + styleRule);
            uploadResult.setKey(pathDescriptor.getRelativePath());
            uploadResult
                    .setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
            uploadResult.setSuffix(pathDescriptor.getExtension());
            uploadResult.setSize(file.getSize());

            // Handle thumbnail
            handleImageMetadata(file, uploadResult, () -> {
                if (ImageUtils.EXTENSION_ICO.equals(pathDescriptor.getExtension())) {
                    uploadResult.setThumbPath(fullPath);
                    return fullPath;
                } else {
                    return StringUtils.isBlank(thumbnailStyleRule) ? fullPath :
                            fullPath + thumbnailStyleRule;
                }
            });
            return uploadResult;
        } catch (Exception e) {
            throw new FileOperationException("附件 " + file.getOriginalFilename() + " 上传失败(腾讯云)", e);
        } finally {
            cosClient.shutdown();
        }
    }

    /**
     * Deletes file.
     *
     * @param key file key must not be null
     * @throws FileOperationException throws when fail to delete the file
     */
    @Override
    public void delete(String key) {
        Assert.notNull(key, "File key must not be blank");

        // Get config
        String region = tencentCosProperty.getRegionOrNonNull();
        String secretId = tencentCosProperty.getSecretIdOrNonNull();
        String secretKey = tencentCosProperty.getSecretKeyOrNonNull();
        String bucketName = tencentCosProperty.getBucketNameOrNonNull();

        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
        Region regionConfig = new Region(region);
        ClientConfig clientConfig = new ClientConfig(regionConfig);

        // Init OSS client
        COSClient cosClient = new COSClient(cred, clientConfig);

        try {
            cosClient.deleteObject(bucketName, key);
        } catch (Exception e) {
            throw new FileOperationException("附件 " + key + " 从腾讯云删除失败", e);
        } finally {
            cosClient.shutdown();
        }
    }

    /**
     * Get attachment type is supported.
     *
     * @return attachment type
     */
    @Override
    public AttachmentType getAttachmentType() {
        return AttachmentType.TENCENTCOS;
    }
}
